from classfile.class_file import ClassFile
from classfile.constant import ConstantUTF8
from classpath.class_entry import ClassBytes
from classpath.class_path import ClassPath
from heap.access_flag import ACC_PUBLIC
from heap.class_klass import Klass
from heap.class_member import Field, Method
from heap.constant_pool import KlassConstantPool
from heap.jstring import JString
from heap.primitive import PRIMITIVE_TYPES
from heap.slot import Slots


class ClassLoader:

    def __init__(self, classpath: ClassPath, parent=None):
        # 构造函数，初始化ClassLoader对象
        self.JAVA_LANG_CLASS = "java/lang/Class"
        self.parent: ClassLoader = parent  # 父类加载器对象
        self.classPath: ClassPath = classpath  # 类路径对象
        # key className  value  klass对象
        self.classCache = {}  # 类缓存字典，用于存储类名与类对象的对应关系

    from heap.class_klass import Klass
    def loadClass(self, className: str) -> Klass:
        javaClassName = className.replace(".", "/")
        if javaClassName in self.classCache.keys():
            print("加载器：{} load class: {} from cache".format(self.__class__.__name__, javaClassName))
            return self.classCache[javaClassName]
        print("加载器：{} load class: {}".format(self.__class__.__name__, javaClassName))
        # 委派给父类加载器
        if self.parent is not None:
            klass = self.parent.loadClass(className)
            if klass is not None:
                return klass

        klass = None
        if className[0:1] == '[':
            klass = self.loadArrayClass(className)
        else:
            klass = self.loadNonArrayClass(className)
        if klass is None:
            return klass
        if self.JAVA_LANG_CLASS in self.classCache.keys():
            if klass.heapClass is None:
                klass.heapClass = self.classCache[self.JAVA_LANG_CLASS].newHeapClass()
                klass.heapClass.extra = klass
        return klass

    def loadArrayClass(self, className) -> Klass:
        kl: Klass = Klass()
        kl.accessFlags = ACC_PUBLIC().value  # 设置访问标志为public
        kl.name = className  # 设置类名为数组类型
        kl.superClassName = "java/lang/Object"
        kl.interfaceNames = ["java/lang/Cloneable", "java/io/Serializable"]  # 设置接口名列表为空
        kl.superClass = self.loadClass(kl.superClassName)  # 设置父类为Object
        kl.interfaces = [self.loadClass("java/lang/Cloneable"), self.loadClass("java/io/Serializable")]  # 设置接口类列表为空
        kl.staticFields = []  # 设置静态字段列表为空
        kl.initStarted = True  # 设置初始化标志为True
        kl.classLoader = self  # 设置类加载器为当前类加载器
        kl.heapClass = self.classCache[self.JAVA_LANG_CLASS].newHeapClass()  # 从类缓存中获取JAVA_LANG_CLASS对应的堆类实例
        kl.heapClass.extra = kl  # 设置堆类实例的额外属性为当前类
        self.classCache[kl.name] = kl  # 将类名和类实例加入类缓存
        return kl

    def loadNonArrayClass(self, className) -> Klass:
        classBytes = self.readClass(className=className)
        if classBytes is None:
            return None
        klass = self.defineClass(classBytes.bytes())
        self.link(klass)
        print("Loaded {} from {}".format(className, classBytes.classEntry().String()))
        self.classCache[klass.name] = klass  # 将类名和类实例加入类缓存
        return klass

    def readClass(self, className: str) -> ClassBytes:
        return self.classPath.readClass(className=className)

    def defineClass(self, bytes) -> Klass:
        cf = ClassFile().parse(bytes=bytes)
        klass: Klass = Klass()
        klass.setAccessFlags(cf)
        cp = cf.constantPool
        klass.name = cp.getConstant(index=cf.thisClass).className.value
        superClass = cp.getConstant(index=cf.superClass)
        if superClass is not None:
            klass.superClassName = superClass.className.value
            klass.superClass = self.loadClass(className=klass.superClassName)
        else:
            if klass.name != "java/lang/Object":
                klass.superClassName = "java/lang/Object"
                klass.superClass = self.loadClass(className=klass.superClassName)
        if len(cf.interfaces) > 0:
            for interface in cf.interfaces:
                interfaceEntry = cp.getConstant(index=interface)
                if interfaceEntry is not None:
                    interfaceName = interfaceEntry.className.value
                    klass.interfaceNames.append(interfaceName)
                    klass.interfaces.append(self.loadClass(className=interfaceName))

        klass.classLoader = self  # 设置类加载器为当前类加载器
        klass.constantPool = KlassConstantPool(klass=klass, constantPool=cp)
        klass.sourceFile = cf.getSourceFile()
        if len(cf.fields) > 0:
            for field in cf.fields:
                f = Field()
                f.klass = klass
                # 名称 修饰符 描述符
                f.copyMemberInfo(field)
                # 常量值属性
                f.copyAttributes(field)
                klass.fields.append(f)
        if len(cf.methods) > 0:
            for method in cf.methods:
                m = Method()
                m.klass = klass
                # 名称 修饰符 描述符
                m.copyMemberInfo(method)
                # code属性
                m.copyAttributes(method)
                # 解析方法的描述符： 参数类型列表，返回值类型
                m.parseDescriptor(m.descriptor)
                # 计算方法的参数槽位数
                m.calcParameterSlotIds()
                # 如果方法是native 方法 根据方法的返回值初始化 操作数栈深度和局部变量表大小
                if m.isNative():
                    m.calcNativeStackAndLocals()
                klass.methods.append(m)
        return klass

    def link(self, klass):
        self.verify(klass)
        self.prepare(klass)

    def verify(self, klass):
        # 验证字节码是否符合规范，是否安全可以执行
        # 这一步会检查操作数栈，局部变量数组等是否合法
        # 例如，检查索引是否越界，检查类型转换是否合法等
        pass

    def prepare(self, klass):
        self.calcInstanceFieldSlotIds(klass)
        self.calcStaticFieldSlotIds(klass)
        self.allowcAndInitStaicVars(klass)

    def calcInstanceFieldSlotIds(self, klass):
        soltId = 0
        if klass.superClass is not None:
            soltId = klass.superClass.instanceSlotCount
        for field in klass.fields:
            if field.isStatic():
                continue
            field.slotId = soltId
            soltId += 1
            if field.isLongOrDouble():
                soltId += 1
        klass.instanceSlotCount = soltId

    def calcStaticFieldSlotIds(self, klass):
        soltId = 0
        for field in klass.fields:
            if not field.isStatic():
                continue
            field.slotId = soltId
            soltId += 1
            if field.isLongOrDouble():
                soltId += 1
        klass.staticSlotCount = soltId

    def allowcAndInitStaicVars(self, klass):
        klass.staticVars = Slots()
        klass.staticVars.makeSlot(maxLocals=klass.staticSlotCount)

        for field in klass.fields:
            if field.isStatic() and not field.isFinal():
                self.initStaticField(klass, field)

    def initStaticField(self, klass, field):
        vars = klass.staticVars
        cp = klass.constantPool
        cpIndex = field.constantValueIndex
        slotId = field.slotId
        if cpIndex > 0:
            if (field.descriptor == "Z"
                    or field.descriptor == "B"
                    or field.descriptor == "C"
                    or field.descriptor == "S"
                    or field.descriptor == "I"):
                vars.setInt(slotId, cp.getConstantValue(cpIndex))
            elif field.descriptor == "J":
                vars.setLong(slotId, cp.getConstantValue(cpIndex))
            elif field.descriptor == "F":
                vars.setFloat(slotId, cp.getConstantValue(cpIndex))
            elif field.descriptor == "D":
                vars.setDouble(slotId, cp.getConstantValue(cpIndex))
            elif field.descriptor == "Ljava/lang/String;":
                str1 = cp.getConstantValue(cpIndex)
                vars.setRef(slotId, JString.internal(klass.classLoader, str1))
            else:
                raise Exception("Unsupported field type: {}".format(field.descriptor))


class BootStrapClassLoader(ClassLoader):

    def __init__(self, classpath: ClassPath):
        super().__init__(classpath)
        # 加载  java.lang.Class
        self.loadBasicClasses()
        # 加载 基本类型的类
        self.loadPrimitiveClasses()

    def loadBasicClasses(self):
        lc = self.loadClass(className=self.JAVA_LANG_CLASS)
        print("初始化加载{},并设置真实的的类指针".format(self.JAVA_LANG_CLASS))
        for name in self.classCache.keys():
            kl: Klass = self.classCache[name]
            if kl.heapClass is None:
                kl.heapClass = lc.newHeapClass()
                kl.heapClass.extra = kl

    def loadPrimitiveClasses(self):
        for primitive in PRIMITIVE_TYPES:
            kl: Klass = Klass()
            kl.accessFlags = ACC_PUBLIC().value  # 设置访问标志为public
            kl.name = primitive.value  # 设置类名为原始类型的值
            kl.classLoader = self  # 设置类加载器为当前类加载器
            kl.initStarted = True  # 设置初始化标志为True

            kl.heapClass = self.classCache[self.JAVA_LANG_CLASS].newHeapClass()  # 从类缓存中获取JAVA_LANG_CLASS对应的堆类实例
            kl.heapClass.extra = kl  # 设置堆类实例的额外属性为当前类
            self.classCache[kl.name] = kl  # 将类名和类实例加入类缓存


# 扩展类加载器
class ExtClassLoader(ClassLoader):

    def __init__(self, classpath: ClassPath, parent: ClassLoader):
        super().__init__(classpath, parent)


# 应用类加载器
class AppClassLoader(ClassLoader):
    def __init__(self, classpath: ClassPath, parent: ClassLoader):
        super().__init__(classpath, parent)
